"use strict";
var __spreadArrays = (this && this.__spreadArrays) || function () {
    for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
    for (var r = Array(s), k = 0, i = 0; i < il; i++)
        for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
            r[k] = a[j];
    return r;
};
var Mvvm = /** @class */ (function () {
    function Mvvm(option) {
        this.$el = option.el;
        this.$data = option.data;
        if (this.$el) {
            //数据劫持
            new Observer(this.$data);
            //模板编译
            new Compile({ el: this.$el, vm: this });
        }
    }
    return Mvvm;
}());
var Dep = /** @class */ (function () {
    function Dep() {
        //存放观察者
        this.watchers = [];
    }
    Dep.prototype.add = function (watcher) {
        this.watchers.push(watcher);
    };
    Dep.prototype.notify = function () {
        this.watchers.forEach(function (item) {
            item.update();
        });
    };
    return Dep;
}());
//观察者
var Watcher = /** @class */ (function () {
    function Watcher(vm, expr, cb) {
        this.vm = vm;
        this.expr = expr;
        this.cb = cb;
        //获取旧值
        this.oldVal = this.getVal();
    }
    Watcher.prototype.getVal = function () {
        Dep.target = this;
        var value = compileUtil.getVal(this.expr, this.vm);
        Dep.target = null;
        return value;
    };
    //更新函数
    Watcher.prototype.update = function () {
        var newVal = compileUtil.getVal(this.expr, this.vm);
        if (newVal != this.oldVal) {
            this.cb(newVal);
        }
    };
    return Watcher;
}());
//vm.$watch(vm,'user.username',(newVal) => {})
//数据劫持
var Observer = /** @class */ (function () {
    function Observer(data) {
        this.observer(data);
    }
    //数据劫持
    Observer.prototype.observer = function (data) {
        if (data && typeof data === 'object') {
            for (var key in data) {
                this.observerData(data, key, data[key]);
            }
        }
    };
    Observer.prototype.observerData = function (data, key, value) {
        this.observer(value);
        var dep = new Dep();
        Object.defineProperty(data, key, {
            set: function (newValue) {
                if (newValue != value) {
                    value = newValue;
                    //通知数据变化
                    dep.notify();
                }
            },
            get: function () {
                Dep.target && dep.add(Dep.target);
                return value;
            }
        });
    };
    return Observer;
}());
//编译模板和数据
var Compile = /** @class */ (function () {
    function Compile(option) {
        this.vm = option.vm;
        this.el = this.isElementNode(option.el) ? option.el : document.querySelector(option.el);
        //编译 把根节点所有的子节点放到文档片段里
        var fragment = this.nodeToFragment(this.el);
        console.log(fragment);
        //文档片段和数据进行编译，初始化视图
        this.compile(fragment);
        this.el.appendChild(fragment);
    }
    //文档片段和数据进行编译，初始化视图
    Compile.prototype.compile = function (node) {
        var _this = this;
        var childNodes = node.childNodes;
        //区分是元素节点还是文本节点
        __spreadArrays(childNodes).forEach(function (item) {
            if (_this.isElementNode(item)) {
                //元素节点
                _this.compileElement(item);
            }
            else {
                //文本节点
                _this.compileText(item);
            }
        });
    };
    Compile.prototype.isDirective = function (attr) {
        // console.log('=====','attr'.startsWith(0,1))
        return attr.substr(0, 2) === 'v-';
    };
    //编译元素节点
    Compile.prototype.compileElement = function (node) {
        var _this = this;
        var attrs = node.attributes;
        console.dir(attrs);
        __spreadArrays(attrs).forEach(function (item) {
            //区分是否是指令属性 type v-model
            var name = item.name, expr = item.value;
            console.log("====", name);
            if (_this.isDirective(name)) {
                var _a = name.split('-'), directive = _a[1];
                //从vm.$data上查找数据，并且替换表达式
                compileUtil[directive](node, expr, _this.vm);
            }
        });
    };
    //编译文本节点
    Compile.prototype.compileText = function (node) {
        var content = node.textContent;
        console.log(content);
        //{{}}
        if (/\{\{(.+?)\}\}/.test(content)) {
            compileUtil.content(node, content, this.vm);
        }
    };
    Compile.prototype.nodeToFragment = function (node) {
        //创建文档片段
        var frament = document.createDocumentFragment();
        var childnodes = node.childNodes;
        __spreadArrays(childnodes).forEach(function (item) {
            frament.appendChild(item);
        });
        return frament;
    };
    Compile.prototype.isElementNode = function (node) {
        return node.nodeType === 1;
    };
    return Compile;
}());
var compileUtil = {
    //根据表达式从vm.$data上获取值
    getVal: function (expr, vm) {
        //user.name
        return expr.split('.').reduce(function (pre, cur) {
            return pre[cur];
        }, vm.$data);
    },
    //设置值
    setVal: function (expr, vm, value) {
        //[user,name]
        expr.split('.').reduce(function (pre, cur, index, arr) {
            if (index === arr.length - 1) {
                pre[cur] = value;
            }
            return pre[cur];
        }, vm.$data);
    },
    model: function (node, expr, vm) {
        var _this = this;
        var val = this.getVal(expr, vm);
        var fn = this.updater.compileModel;
        fn(node, val);
        //生成观察者
        new Watcher(vm, expr, function (newVal) {
            fn(node, newVal);
        });
        //添加input事件
        node.addEventListener('input', function (e) {
            //vm.$data expr  e.target.value
            _this.setVal(expr, vm, e.target.value);
        });
    },
    content: function (node, content, vm) {
        var _this = this;
        var val = content.replace(/\{\{(.+?)\}\}/g, function () {
            var argv = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                argv[_i] = arguments[_i];
            }
            console.log(argv);
            new Watcher(vm, argv[1], function (newVal) {
                fn(node, newVal);
            });
            return _this.getVal(argv[1], vm);
        });
        var fn = this.updater.compileText;
        fn(node, val);
    },
    updater: {
        compileModel: function (node, value) {
            node.value = value;
        },
        compileText: function (node, value) {
            node.textContent = value;
        }
    }
};
